/*
 * Copyright (C) 2015 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package retrofit2;

import okhttp3.ResponseBody;

import javax.annotation.Nullable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;

import static retrofit2.Utils.getRawType;
import static retrofit2.Utils.methodError;

/**
 * 一个把接口上的方法，转换为Http调用的ServiceMethod
 */
abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
    private final RequestFactory requestFactory;
    private final okhttp3.Call.Factory callFactory;
    private final Converter<ResponseBody, ResponseT> responseConverter;

    HttpServiceMethod(
            RequestFactory requestFactory,
            okhttp3.Call.Factory callFactory,
            Converter<ResponseBody, ResponseT> responseConverter) {
        this.requestFactory = requestFactory;
        this.callFactory = callFactory;
        this.responseConverter = responseConverter;
    }

    /**
     * 解析方法上的注解
     */
    static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
            Retrofit retrofit, Method method, RequestFactory requestFactory) {
        //获取方法上的注解和返回值
        Annotation[] annotations = method.getAnnotations();
        Type adapterType = method.getGenericReturnType();

        //找到支持适配返回值类型的CallAdapter
        CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method, adapterType, annotations);
        Type responseType = callAdapter.responseType();
        //泛型不能为okhttp3的Response，你会不会是想要ResponseBody
        if (responseType == okhttp3.Response.class) {
            throw methodError(
                    method,
                    "'"
                            + getRawType(responseType).getName()
                            + "' is not a valid response body type. Did you mean ResponseBody?");
        }
        //Response必须要有泛型
        if (responseType == Response.class) {
            throw methodError(method, "Response must include generic type (e.g., Response<String>)");
        }
        // TODO support Unit for Kotlin?
        //顶层方法必须没有返回值，例如public void static main() {}
        if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
            throw methodError(method, "HEAD method must use Void as response type.");
        }

        //找到对应的转换器
        Converter<ResponseBody, ResponseT> responseConverter = createResponseConverter(retrofit, method, responseType);

        okhttp3.Call.Factory callFactory = retrofit.callFactory;
        //创建支持适配工作的HttpServiceMethod，就是复写adapt()方法，并使用适配器进行转换
        return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    }

    /**
     * 找到支持适配返回值类型的CallAdapter
     */
    private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
            Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
        try {
            //noinspection unchecked
            return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
        } catch (RuntimeException e) { // Wide exception range because factories are user code.
            throw methodError(method, e, "Unable to create call adapter for %s", returnType);
        }
    }

    /**
     * 找到对应的转换器
     */
    private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter(
            Retrofit retrofit, Method method, Type responseType) {
        Annotation[] annotations = method.getAnnotations();
        try {
            return retrofit.responseBodyConverter(responseType, annotations);
        } catch (RuntimeException e) { // Wide exception range because factories are user code.
            throw methodError(method, e, "Unable to create converter for %s", responseType);
        }
    }

    /**
     * 执行方法调用
     */
    @Override
    final @Nullable
    ReturnT invoke(Object[] args) {
        //创建Call实例
        Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
        //进行适配后返回结果
        return adapt(call, args);
    }

    /**
     * 适配方法
     */
    protected abstract @Nullable
    ReturnT adapt(Call<ResponseT> call, Object[] args);

    /**
     * 支持进行适配的HttpServiceMethod子类
     */
    static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
        private final CallAdapter<ResponseT, ReturnT> callAdapter;

        CallAdapted(
                RequestFactory requestFactory,
                okhttp3.Call.Factory callFactory,
                Converter<ResponseBody, ResponseT> responseConverter,
                CallAdapter<ResponseT, ReturnT> callAdapter) {
            super(requestFactory, callFactory, responseConverter);
            this.callAdapter = callAdapter;
        }

        @Override
        protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
            //使用CallAdapter适配请求的返回值
            return callAdapter.adapt(call);
        }
    }
}
